- 
                Notifications
    
You must be signed in to change notification settings  - Fork 1.7k
 
Implement eth/65 (EIP-2464) #11626
base: master
Are you sure you want to change the base?
Implement eth/65 (EIP-2464) #11626
Conversation
| } | ||
| 
               | 
          ||
| fn pooled_transaction(&self, hash: H256) -> Option<Arc<VerifiedTransaction>> { | ||
| self.importer.miner.transaction(&hash) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The importer separation was initially there to try to separate import pipeline from the client (which would become responsible only for queries). However I'm not sure how much it's still relevant, so I don't believe any change is required here, just giving some context.
| snapshot = { path = "../snapshot" } | ||
| trace-time = "0.1" | ||
| triehash-ethereum = { version = "0.2", path = "../../util/triehash-ethereum" } | ||
| transaction-pool = "2" | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it might be worth to introduce some abstraction to avoid introducing this dependency. For instance client-traits could have trait QueuedTransactionthat would include all the things thatsync/network` needs.
| difficulty, | ||
| latest_hash, | ||
| genesis, | ||
| unsent_pooled_hashes: if eth_protocol_version >= ETH_PROTOCOL_VERSION_65.0 { Some(io.chain().transactions_to_propagate().into_iter().map(|tx| *tx.hash()).collect()) } else { None }, | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Using transactions_to_propagate here is going to be extremely inefficient. This method creates an ordered iterator over all tranasctions in the pool, and it's quite costly - especially if the pool is large.
We support a more efficient way to query all hashes from the transaction pool (see Miner::pending_transaction_hashes) and I think it's worth to use it here as well.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think there is also some recommended limit in the spec, isn't there?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should I somehow expose dyn MinerService here then to take advantage of pending_transaction_hashes?
| let mut affected = false; | ||
| let mut packet = RlpStream::new(); | ||
| packet.begin_unbounded_list(); | ||
| if let Some(s) = &mut peer.unsent_pooled_hashes { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As I said earlier we should be using last_sent_hashes here as well, we already have similar logic in transaction propagation.
Actually this whole method might not be needed at all. We should use current transaction propagation logic and this new ETH65 consolidation protocol should only be used to LIMIT the transactions we are sending to hashes that were actually requested (or not seen) by the other peer.
| 
               | 
          ||
| /// Respond to GetPooledTransactions request | ||
| fn return_pooled_transactions(io: &dyn SyncIo, r: &Rlp, peer_id: PeerId) -> RlpResponseResult { | ||
| const LIMIT: usize = 256; | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We already have a bunch of constants like this, we should keep them nearby with proper documentation.
        
          
                ethcore/sync/src/chain/supplier.rs
              
                Outdated
          
        
      | const LIMIT: usize = 256; | ||
| 
               | 
          ||
| let transactions = r.iter().take(LIMIT).filter_map(|v| { | ||
| v.as_val::<H256>().ok().and_then(|hash| io.chain().pooled_transaction(hash)) | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
As mentioned earlier I think the method should support requesting a batch of transactions from the pool instead of going one-by-one.
| 
               | 
          ||
| let added = transactions.len(); | ||
| let mut rlp = RlpStream::new_list(added); | ||
| for tx in transactions { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We have methods to make sure the package does not end up oversized, please take a look how we construct transaction packages during propagation I think the same logic should apply here (instead of having new arbitrary limit of 256 transactions).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 256 comes from the EIP and is a suggestion. Defntly should be combined with io.payload_soft_limit() (4Mb I think) like we do in other places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Left a few comments, but I think it'd be best to address Tomeks concerns before digging in further; looks like there might be some significant changes coming.
| Ok(Some((BlockHeadersPacket.id(), rlp))) | ||
| } | ||
| 
               | 
          ||
| /// Respond to GetPooledTransactions request | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can be improved.
| 
               | 
          ||
| let added = transactions.len(); | ||
| let mut rlp = RlpStream::new_list(added); | ||
| for tx in transactions { | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The 256 comes from the EIP and is a suggestion. Defntly should be combined with io.payload_soft_limit() (4Mb I think) like we do in other places.
| asking_blocks: Vec<H256>, | ||
| /// Holds requested header hash if currently requesting block header by hash | ||
| asking_hash: Option<H256>, | ||
| /// Holds requested transaction IDs | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| /// Holds requested transaction IDs | |
| /// Hash of the transaction we're requesting from the peer | 
?
| /// Holds requested header hash if currently requesting block header by hash | ||
| asking_hash: Option<H256>, | ||
| /// Holds requested transaction IDs | ||
| asking_pooled_transactions: Option<Vec<H256>>, | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| asking_pooled_transactions: Option<Vec<H256>>, | |
| asking_pooled_transaction: Option<Vec<H256>>, | 
| /// Sync start timestamp. Measured when first peer is connected | ||
| sync_start_time: Option<Instant>, | ||
| /// Transactions propagation statistics | ||
| transactions_stats: TransactionsStats, | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need changes here too to account for eth/65 traffic? Would be good to have good data on protocol usage.
        
          
                ethcore/sync/src/chain/mod.rs
              
                Outdated
          
        
      | sync_start_time: Option<Instant>, | ||
| /// Transactions propagation statistics | ||
| transactions_stats: TransactionsStats, | ||
| /// Unfetched transactions | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are these txs that we have seen announced but not fetched yet? Are they txs that we know we do not have or?
| snapshot: Snapshot::new(), | ||
| sync_start_time: None, | ||
| transactions_stats: TransactionsStats::default(), | ||
| unfetched_pooled_transactions: Default::default(), | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
| unfetched_pooled_transactions: Default::default(), | |
| unfetched_pooled_transactions: H256FastMap::default(), | 
(to me it's more readable to see the actual type, but maybe it's just me)
| /// Peer best block hash | ||
| latest_hash: H256, | ||
| /// Unpropagated tx pool hashes | ||
| unsent_pooled_hashes: Option<H256FastSet>, | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is the point of Option here? Is None different from the hash set being empty, semantically? I mean to say: isn't it unlikely we'll be running with this set None during normal operation, and if so there's not much of a space saving?
| 
           @tomusdrw Is it correct that   | 
    
3374ed3    to
    b4af110      
    Compare
  
    
This PR adds support for Ethereum protocol version 65 (eth/65) described in EIP-2464. This is a pretty rough draft and can definitely be improved, but I hope the overall direction is good.
Fixes #11500